package com.benshou.bcss.common.cglib;

import com.benshou.bcss.common.j17common.annotation.NotNull;
import com.benshou.bcss.common.j17common.annotation.Nullable;
import lombok.NonNull;
import org.springframework.cglib.beans.BeanCopier;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * 基于 Cglib 框架的属性拷贝工具
 *
 * <p>
 *     本工具类提供了高性能的 Java 对象属性拷贝功能，内部使用 Spring 的 Cglib BeanCopier 实现，
 *     并通过缓存 BeanCopier 实例来提高性能。适用于同名属性之间的拷贝。
 * </p>
 * <p>
 *     注意：本工具有如下受限：
 *     1. 不支持类型转换，需提前确保字段类型一致
 *     2. 默认不调用目标对象构造函数（浅拷贝）
 * </p>
 * @author <a href="mailto:liuzhenbang@benshou.com">Liu Zhenbang</a>
 */
public class CglibCopy {

    /**
     * BeanCopier 缓存，用于存储已创建的 BeanCopier 实例，提高性能
     */
    private static final Map<_CopierKey, BeanCopier> COPIER_CACHE = new ConcurrentHashMap<>();

    /**
     * 将源对象的属性拷贝到目标对象
     *
     * @param from 源对象
     * @param fromCls 源对象类型，如果为 null 则自动获取
     * @param to 目标对象
     * @param toCls 目标对象类型，如果为 null 则自动获取
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     */
    public static <F, T> void copy(F from, Class<? super F> fromCls, T to, Class<? super T> toCls) {
        if (from == null || to == null) {
            return;
        }

        if (fromCls == null) {
            @SuppressWarnings("unchecked")
            var fCls = (Class<F>) from.getClass();
            fromCls = fCls;
        }

        if (toCls == null) {
            @SuppressWarnings("unchecked")
            var tCls = (Class<T>) to.getClass();
            toCls = tCls;
        }

        var copier = getOrCreateBeanCopier(fromCls, toCls);
        copier.copy(from, to, null);
    }

    /**
     * 将源对象的属性拷贝到目标对象（简化版本）
     *
     * @param from 源对象
     * @param to 目标对象
     */
    public static void copy(Object from, Object to) {
        copy(from, null, to, null);
    }

    /**
     * 创建目标对象实例并将源对象属性拷贝到其中
     *
     * @param from 源对象
     * @param fromCls 源对象类型，如果为 null 则自动获取
     * @param toCreator 目标对象创建器
     * @param toCls 目标对象类型，如果为 null 则自动获取
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     * @return 拷贝属性后的目标对象
     */
    public static <F, T> T copyCreating(
            F from,
            Class<? super F> fromCls,
            @NonNull Supplier<T> toCreator,
            Class<? super T> toCls) {
        var to = toCreator.get();
        if (from == null || to == null) {
            return to;
        }

        copy(from, fromCls, to, toCls);
        return to;
    }

    /**
     * 创建目标对象实例并将源对象属性拷贝到其中（简化版本）
     *
     * @param from 源对象
     * @param toCreator 目标对象创建器
     * @param <T> 目标对象类型
     * @return 拷贝属性后的目标对象
     */
    public static <T> T copyCreating(Object from, @NonNull Supplier<T> toCreator) {
        return copyCreating(from, null, toCreator, null);
    }

    /**
     * 若源对象非 null ，创建目标对象实例并将源对象属性拷贝到其中，否则直接返回 null
     *
     * @param from 源对象，如果为 null 则直接返回 null
     * @param fromCls 源对象类型，如果为 null 则自动获取
     * @param toCreator 目标对象创建器
     * @param toCls 目标对象类型，如果为 null 则自动获取
     * @param <F> 源对象类型
     * @return 拷贝属性后的目标对象
     * @param <T> 目标对象类型
     */
    @Nullable
    public static <F, T> T copyCreatingOrNull(
            F from,
            Class<? super F> fromCls,
            @NonNull Supplier<T> toCreator,
            Class<? super T> toCls) {
        T to;
        if (from == null || (to = toCreator.get()) == null) {
            return null;
        }

        copy(from, fromCls, to, toCls);
        return to;
    }

    /**
     * 若源对象非 null ，创建目标对象实例并将源对象属性拷贝到其中，否则直接返回 null（简化版本）
     *
     * @param from 源对象，如果为 null 则直接返回 null
     * @param toCreator 目标对象创建器
     * @return 拷贝属性后的目标对象
     * @param <T> 目标对象类型
     */
    @Nullable
    public static <T> T copyCreatingOrNull(Object from, @NonNull Supplier<T> toCreator) {
        return copyCreatingOrNull(from, null, toCreator, null);
    }

    /**
     * 批量：将源对象的元素属性，批量拷贝到新创建的目标对象中
     *
     * @param froms 列表：源对象
     * @param fromCls 源对象类型，如果为 null 则自动获取
     * @param toCreator 目标对象创建器
     * @param toCls 目标对象类型，如果为 null 则自动获取
     * @param useSameCopier 是否使用同一个 copier 进行拷贝。
     *                      这意味着在列表的整个拷贝周期中，一旦生成/获取到有效的 copier ，则后续所有元素的拷贝都将使用此 copier 。
     *                      当你的列表中所有的元素都是同种类型（而非类型的子类）时，
     *                      打开此选项，会提升执行速度（因为无需重复寻找合适的 copier ）。
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     * @return 列表：拷贝属性后的目标对象
     */
    public static <F, T> List<T> copyCreatingMulti(
            Collection<F> froms,
            final Class<? super F> fromCls,
            @NonNull Supplier<? extends T> toCreator,
            final Class<? super T> toCls,
            boolean useSameCopier) {
        if (froms == null || froms.isEmpty()) {
            return new ArrayList<>(0);
        }

        var tos = new ArrayList<T>(froms.size());
        var copier = fromCls == null || toCls == null ? null : getOrCreateBeanCopier(fromCls, toCls);
        for (F from : froms) {
            var to = toCreator.get();
            if (from != null && to != null) {
                BeanCopier currentCopier;
                if ((currentCopier = copier) == null) {
                    Class<? super F> currentFromCls;
                    if ((currentFromCls = fromCls) == null) {
                        @SuppressWarnings("unchecked")
                        var fCls = (Class<F>) from.getClass();
                        currentFromCls = fCls;
                    }

                    Class<? super T> currentToClass;
                    if ((currentToClass = toCls) == null) {
                        @SuppressWarnings("unchecked")
                        var tCls = (Class<T>) to.getClass();
                        currentToClass = tCls;
                    }

                    currentCopier = getOrCreateBeanCopier(currentFromCls, currentToClass);

                    if (useSameCopier) {
                        copier = currentCopier;
                    }
                }

                currentCopier.copy(from, to, null);
            }

            tos.add(to);
        }

        return tos;
    }

    /**
     * 批量：将源对象的元素属性批量拷贝到新创建的目标对象中（简化版本）
     *
     * @param froms 列表：源对象
     * @param toCreator 目标对象创建器
     * @param <F> 源对象类型
     * @param <T> 目标对象类型
     * @return 列表：拷贝属性后的目标对象
     */
    public static <F, T> List<T> copyCreatingMulti(
            Collection<F> froms,
            @NonNull Supplier<? extends T> toCreator) {
        return copyCreatingMulti(froms, null, toCreator, null, true);
    }

    /**
     * 获取或创建 BeanCopier 实例
     * 
     * @param fromCls 源对象类型
     * @param toCls 目标对象类型
     * @return BeanCopier实例
     */
    private static BeanCopier getOrCreateBeanCopier(@NotNull Class<?> fromCls, @NotNull Class<?> toCls) {
        return COPIER_CACHE.computeIfAbsent(
                new _CopierKey(fromCls, toCls),
                key -> BeanCopier.create(key.fromCls, key.toCls, false));
    }

    /**
     * BeanCopier 缓存 Key
     */
    private record _CopierKey(
            Class<?> fromCls,
            Class<?> toCls) {

    }
}
